package com.neo.design.chain;

import com.neo.design.chain.approve.ApproveRequest;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.LinkedList;
import java.util.List;

/**
 * 全局业务链管理中心<p/>
 * 系统中定义和注册的业务链，以每个业务链为一个链对象，业务链管理器负责集中进行维护和处理，
 * 并且提供统一的处理入口，处理入口： {@link ChainContextManager#handleRequest(ChainRequest)} 。<p/>
 * 当然，也可以直接通过调用业务链对象进行处理，如： {@link com.neo.design.chain.approve.ApproveContext#handleRequest(ApproveRequest)}
 * <p/>
 * 这里维护的管理中心，可以理解为全局的业务链管理器，通过请求参数的类型{@link ChainRequest}进行业务链的分发，
 * 分发逻辑是通过 {@link ChainContext#match(ChainRequest)} 方法进行匹配，匹配成功后，
 * 通过具体的 {@link ChainContext#handleRequest(ChainRequest)} 实现类方法进行处理，也就是具体的业务链。<p/>
 * <p>
 * 调用方式示例：<pre>
 *  // 注入全局业务链管理中心
 *  private ChainContextManager chainContextManager;
 *
 *  public Result<ChainResult> handle***(ApproveRequest request) {
 *      return Result.ok(chainContextManager.handleRequest(request));
 *  }</pre>
 * <p>
 * <p/>
 * 关于 {@link ApplicationContextAware} ，这里根据自己的习惯进行实现，本人习惯了这种用法
 *
 * @author kevin
 */
@Component
public class ChainContextManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    /**
     * 业务链集合：
     */
    private final List<ChainContext<ChainRequest, ChainResult>> chains = new LinkedList<>();

    /**
     * 初始化业务链集合<p/>
     * <p>
     * <p>
     * {@link PostConstruct} 注解的作用以及用法，这里不做解释，自行百度或B站上找教程<p/>
     * 还有一种初始化方法为：实现{@link org.springframework.beans.factory.SmartInitializingSingleton#afterSingletonsInstantiated()}方法
     */
    @PostConstruct
    @SuppressWarnings("unchecked")
    private void init() {
        applicationContext.getBeansOfType(ChainContext.class).forEach((key, value) -> chains.add(value));
    }

    /**
     * 业务处理请求入口<p/>
     * 当管理中初始化完成后，系统内注册的业务链会注入到当前对象中的业务链集合中，当有请求进来时，
     * 会根据请求参数的类型进行匹配，匹配成功后，会调用对应的业务链进行处理。
     *
     * @param request 业务请求对象
     * @param <T>     具体的业务请求对象类型，必须是{@link ChainRequest}的子类
     * @param <R>     业务处理返回对象，必须是{@link ChainResult}以及其子类的类型
     * @return 处理结果
     */
    @SuppressWarnings("unchecked")
    public <T extends ChainRequest, R extends ChainResult> R handleRequest(T request) {
        Assert.notEmpty(chains, "初始化失败");
        return chains.stream().filter(chain -> chain.match(request))
                .findFirst()
                .map(chain -> (R) chain.handleRequest(request)).orElse(null);
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

}
